package controllers

import command.CommandExec

import javax.inject.Inject
import dao._
import org.apache.commons.math3.stat.StatUtils
import play.api.libs.json._
import play.api.mvc._

import scala.collection.mutable.ArrayBuffer
import scala.math._
import models.Tables._
import tool.{FormTool, Tool}
import utils.Utils

import java.io.File
import java.nio.file.Files
import scala.collection.mutable
import scala.concurrent.ExecutionContext.Implicits.global
import implicits.Implicits._
import tool.Pojo.CommandData

/**
  * Created by yz on 2017/6/2.
  */
class SearchController @Inject()(longRNAProfileDao: LongRNAProfileDao,
                                 longrnageneannotationDao: BrowseLongRnaDao, cirRNAProfileDao: CirRNAProfileDao,
                                 circAllPositionsDao: BrowseCircRnaDao, cc: ControllerComponents)(
                                implicit val modeDao: ModeDao
) extends
  AbstractController(cc) {

  def toIndex = Action {
    Ok(views.html.search.index())
  }

  def downloadPlotData = Action { implicit request =>
    val kind=FormTool.kindForm.bindFromRequest().get.kind
    val file=new File(Tool.dataDir,s"searchResult_mean_${kind}.csv")
    Ok.sendFile(file).withHeaders(
      CACHE_CONTROL -> "max-age=3600",
      CONTENT_DISPOSITION -> ("attachment; filename=" + file.getName),
      CONTENT_TYPE -> "application/x-download"
    )
  }

  def getLinearImage = Action {
    implicit request =>
      val startTime = System.currentTimeMillis()
      val data = FormTool.geneSymbolForm.bindFromRequest().get
      val kind=FormTool.kindForm.bindFromRequest().get.kind
      val geneSymbol = data.geneSymbol
      val tmpDir = Tool.createTempDirectory("tmpDir")
      val dataFile=new File(Tool.dataDir,s"searchResult_mean_${kind}.txt")
      val pdfFile=new File(tmpDir,"out.pdf")
      val pngFile=new File(tmpDir,"out.png")
      val command =
        s"""
           |${"Rscript".condaPath()} ${Tool.rPath.unixPath}/searchResult_mean_linePoint.R --d ${dataFile.unixPath} --t '${geneSymbol}' --targetType ${kind}  --o ${pdfFile.unixPath}
           |convert  -density 300 -background white -alpha remove -alpha off ${pdfFile.getAbsoluteFile.unixPath} ${pngFile.getAbsoluteFile.unixPath}
           |""".stripMargin
      val commandExec = CommandExec().exec { b =>
        CommandData(tmpDir, command)
      }
      if (commandExec.isSuccess) {
        println("exec complete:", Utils.getTime(startTime))
        val base64 = pngFile.base64
        val pdfBase64 = pdfFile.base64
        Tool.deleteDirectory(tmpDir)
        Ok(Json.obj("valid" -> true, "base64" -> base64, "pdfBase64" -> pdfBase64))
      } else {
        Tool.deleteDirectory(tmpDir)
        Ok(Json.obj("valid" -> false, "message" -> commandExec.errorInfo))
      }
  }

  def getHeatmapImage = Action {
    implicit request =>
      val startTime = System.currentTimeMillis()
      val data = FormTool.geneSymbolForm.bindFromRequest().get
      val kind=FormTool.kindForm.bindFromRequest().get.kind
      val geneSymbol = data.geneSymbol
      val tmpDir = Tool.createTempDirectory("tmpDir")
      val dataFile=new File(Tool.dataDir,s"searchResult_mean_${kind}.txt")
      val pdfFile=new File(tmpDir,"out.pdf")
      val pngFile=new File(tmpDir,"out.png")
      val command =
        s"""
           |${"Rscript".condaPath()} ${Tool.rPath.unixPath}/searchResult_mean_heatmap.R --d ${dataFile.unixPath} --t '${geneSymbol}' --targetType ${kind} --o ${pdfFile.unixPath}
           |convert  -density 300 -background white -alpha remove -alpha off ${pdfFile.getAbsoluteFile.unixPath} ${pngFile.getAbsoluteFile.unixPath}
           |""".stripMargin
      val commandExec = CommandExec().exec { b =>
        CommandData(tmpDir, command)
      }
      if (commandExec.isSuccess) {
        println("exec complete:", Utils.getTime(startTime))
        val base64 = pngFile.base64
        val pdfBase64 = pdfFile.base64
        Tool.deleteDirectory(tmpDir)
        Ok(Json.obj("valid" -> true, "base64" -> base64, "pdfBase64" -> pdfBase64))
      } else {
        Tool.deleteDirectory(tmpDir)
        Ok(Json.obj("valid" -> false, "message" -> commandExec.errorInfo))
      }
  }

  def getByGeneId(id: String): Action[AnyContent] = Action.async { implicit request =>
    val ids=id.split(",").map(_.trim).distinct
    longrnageneannotationDao.selectByGeneNames(ids).map { x =>
      val array = x.map { y =>
        getLongArray(y)
      }
      Ok(Json.toJson(array))
    }
  }

  def getLongArray(y: BrowseLongRnaRow) = {
    //    val circbaselinks = if (y.circbaselinks.toUpperCase == "NA") "NA" else "<a target='_blank' href='" + y.circbaselinks + "'>" +
    //      y.circbaseid + "</a>"
//    val genenameStr = "<a target='_blank' href='" + routes.SearchController.getMoreInfo(y.genename) + "'>" + y.genename + "</a>"
//    val detectionfrequencyStr = y.detectionfrequency.toString
//    val relatedcircsStr = if (y.relatedcircs.toUpperCase == "NA") "0" else {
//      "<a href='" + routes.SearchController.circInfo(y.relatedcircs) + "' target='_blank'>" + y.relatedcircs.split(",").size + "</a>"
//    }
//    val image = "<a href='#' onclick=\"boxPlot('" + y.genename + "','boxPlotByGeneId')\"><span class='glyphicon glyphicon-picture'></span></a>"
//    val specifictyScoreStr = if (y.specificityscore == "NA") "NA" else y.specificityscore.toDouble.formatted("%.3f").toString
//
//    Json.obj("genename" -> genenameStr, "genetype" -> y.genetype, "relatedcircs" -> relatedcircsStr, "detectionfrequency" -> detectionfrequencyStr,
//      "expressionrank" -> y.expressionrank, "specifiexpressiontissue" -> y.specifiexpressiontissue, "specificityscore" -> specifictyScoreStr,
//      "samplegroup" -> y.samplegroup, "diffgroup" -> y.diffgroup, "validation" -> y.validation, "image" -> image)
    Json.obj()
  }

  def sortLongByType(x: Seq[LongrnaprofileRow]) = {
    val newX = ArrayBuffer[LongrnaprofileRow]()
    Utils.array.foreach { y =>
      newX ++= x.filter(_.samplename == y)
    }
    newX
  }

  def sortCircByType(x: Seq[CircrnaprofileRow]) = {
    val newX = ArrayBuffer[CircrnaprofileRow]()
    Utils.array.foreach { y =>
      newX ++= x.filter(_.samplename == y)
    }
    newX
  }

  def heatmap(id: String): Action[AnyContent] = Action.async { implicit request =>
    val genes = id.split(",").map(_.trim)
    val longRNAProfiles = longRNAProfileDao.selectByGeneId(id)
    longRNAProfiles.map(tmpX => Ok {
      val x = sortLongByType(tmpX)
      val samples = x.map(_.samplename).distinct
      val samples1 = x.map(x => x.samplename + "(" + x.`type` + ")").distinct
      val newGenes = genes.map { gene =>
        x.filter(_.geneid.toUpperCase == gene.toUpperCase).head.geneid
      }
      val expressions = for (i <- samples.indices; j <- genes.indices) yield {
        val longRNAProfile = x.filter(_.geneid.toUpperCase == genes(j).toUpperCase).find(_.samplename == samples(i))
        longRNAProfile match {
          case Some(longRNAProfile1) => Array(i, j, log2(longRNAProfile1.value + 1))
          case _ => Array(i, j, log2(0.0 + 1))
        }
      }
      val jsons = Array(Json.obj("expression" -> expressions, "treatment" -> samples1, "gt" -> newGenes))
      Json.toJson(jsons)
    })
  }

  def linear(id: String): Action[AnyContent] = Action.async { implicit request =>
    val genes = id.split(",").map(_.trim)
    val longRNAProfiles = longRNAProfileDao.selectByGeneId(id)
    longRNAProfiles.map(tmpX => Ok {
      var map = Map[String, String]()
      val x = sortLongByType(tmpX)
      val infos = genes.map { gene =>
        val line = x.filter(_.geneid.toUpperCase == gene.toUpperCase)
        val sampleNames = line.map(_.samplename)
        map ++= line.map(y => (y.samplename, y.`type`)).toMap
        val datas = line.map(_.value)
        val name = line.head.geneid
        (sampleNames, datas, name)
      }
      val maxCategory = infos.flatMap(_._1).distinct.map(x => x + "(" + map(x) + ")")
      val jsons = infos.map(y => Json.obj("category" -> y._1, "data" -> y._2, "name" -> y._3))
      val finalJsons = Json.obj("infos" -> jsons, "maxCategory" -> maxCategory)
      Json.toJson(finalJsons)
    })
  }

  def boxPlotByGeneId(id: String): Action[AnyContent] = Action.async { implicit request =>
    val longRNAProfiles = longRNAProfileDao.selectByGeneId(id)
    longRNAProfiles.map(x => Ok {
      val typeX = x.groupBy(_.`type`)
      var newTypeX = mutable.LinkedHashMap[String, Seq[LongrnaprofileRow]]()
      newTypeX ++= typeX.filter(_._1 == "NP")
      newTypeX ++= typeX.filter(_._1 == "CHD")
      newTypeX ++= typeX.filter(_._1 == "CRC")
      newTypeX ++= typeX.filter(_._1 == "HCC")
      newTypeX ++= typeX.filter(_._1 == "PAAD")
      newTypeX ++= typeX.filter(_._1 == "WhB")
      val typeLong = newTypeX.map {
        case (kind, longs) =>
          val values = longs.map(_.value).toArray
          val minValue = values.min
          val Q1 = StatUtils.percentile(values, 25)
          val Q2 = StatUtils.percentile(values, 50)
          val Q3 = StatUtils.percentile(values, 75)
          val maxValue = values.max
          Array(kind, minValue.toString, Q1.toString, Q2.toString, Q3.toString, maxValue.toString)
      }
      val jsons = Json.obj("ev" -> typeLong.map(_.drop(1).map(_.toDouble)), "tissue" -> typeLong.map(_ (0)))
      Json.toJson(jsons)
    })
  }

  def circBoxPlot(id: String): Action[AnyContent] = Action.async { implicit request =>
    val ids=id.split(",").map(_.trim).distinct
    val longRNAProfiles = cirRNAProfileDao.selectByCircIds(ids)
    longRNAProfiles.map(x => Ok {
      val typeX = x.groupBy(_.`type`)
      var newTypeX = mutable.LinkedHashMap[String, Seq[CircrnaprofileRow]]()
      newTypeX ++= typeX.filter(_._1 == "NP")
      newTypeX ++= typeX.filter(_._1 == "CHD")
      newTypeX ++= typeX.filter(_._1 == "CRC")
      newTypeX ++= typeX.filter(_._1 == "HCC")
      newTypeX ++= typeX.filter(_._1 == "PAAD")
      newTypeX ++= typeX.filter(_._1 == "WhB")
      val typeLong = newTypeX.map {
        case (kind, longs) =>
          val values = longs.map(_.value).toArray
          val minValue = values.min
          val Q1 = StatUtils.percentile(values, 25)
          val Q2 = StatUtils.percentile(values, 50)
          val Q3 = StatUtils.percentile(values, 75)
          val maxValue = values.max
          Array(kind, minValue.toString, Q1.toString, Q2.toString, Q3.toString, maxValue.toString)
      }
      val jsons = Json.obj("ev" -> typeLong.map(_.drop(1).map(_.toDouble)), "tissue" -> typeLong.map(_ (0)))
      Json.toJson(jsons)
    })
  }

  def getCircMoreInfo(id: String): Action[AnyContent] = Action.async { implicit request =>
    val ids=id.split(",").map(_.trim).distinct
    val long = circAllPositionsDao.selectByCircIds(ids)
    long.map { x =>
      Ok {
        val trueCirc = x.head
        views.html.search.circMoreInfo(trueCirc)
      }
    }

  }

  def log2(x: Double): Double = log10(x) / log10(2.0)

}